package fudan.se.project.util.entitymanage;


import fudan.se.project.domain.User;
import fudan.se.project.domain.conference.Conference;
import fudan.se.project.domain.conference.Topic;
import fudan.se.project.domain.conference.post.Post;
import fudan.se.project.domain.conference.post.Rebuttal;
import fudan.se.project.domain.conference.post.Reply;
import fudan.se.project.domain.conference.production.Paper;
import fudan.se.project.domain.conference.production.ReviewInfo;
import fudan.se.project.domain.conference.production.Writer;
import fudan.se.project.domain.conference.role.Author;
import fudan.se.project.domain.conference.role.Chair;
import fudan.se.project.domain.conference.role.PcMember;
import fudan.se.project.service.infrastructure.func.conference.resoure.paperalloc.PaperAllocateService;
import fudan.se.project.util.repository.RepositoryUtil;

import java.util.*;

public class ConferenceManagementUtil extends EntityManagementUtil {

    private final PaperAllocateService paperAllocateService;
    private final RepositoryUtil util;


    // conference storage
    private final HashMap<String, Long> cfs = new HashMap<>();

    // topic storage
    private final HashMap<String, HashMap<String, Long>> tpcs = new HashMap<>();

    // pps storage
    private final HashMap<String, HashMap<String, Long>> pps = new HashMap<>();

    public ConferenceManagementUtil(PaperAllocateService paperAllocateService, RepositoryUtil util) {
        this.paperAllocateService = paperAllocateService;
        this.util = util;
    }

    private User queryUser(String username) {
        return util.userRepository.findByUsername(username);
    }

    public Conference createNewConference(String confname) {
        final Date date = new Date();
        Conference conference = new Conference(confname, confname, confname, date, date, date, date);
        conference = util.conferenceRepository.save(conference);
        cfs.put(confname, conference.getId());

        tpcs.put(confname, new HashMap<>());
        pps.put(confname, new HashMap<>());
        return conference;
    }

    public Conference queryConference(String confname) {
        return util.conferenceRepository.findById(cfs.get(confname).longValue());
    }

    public Conference changeState(String confname, Conference.ConferenceState state) {
        final Conference conference = queryConference(confname);
        conference.setState(state);
        return util.conferenceRepository.save(conference);
    }

    public Conference changeStage(String confname, Conference.ConferenceStage stage) {
        final Conference conference = queryConference(confname);
        conference.setStage(stage);
        return util.conferenceRepository.save(conference);
    }

    //region chair add & query
    public Chair addChair(String confname, String username) {
        Chair chair = new Chair(queryUser(username), queryConference(confname));
        chair = util.chairRepository.save(chair);
        return chair;
    }

    public Chair queryChair(String confname, String username) {
        return util.chairRepository.
                findByUserAndConference(queryUser(username), queryConference(confname));
    }
    //endregion

    //region pcmember add & query
    public PcMember addPcMember(String confname, String username, String... topics) {
        PcMember pcMember = new PcMember(queryUser(username), queryConference(confname));
        final List<Topic> topicList = queryTopic(confname, topics);
        pcMember.addTopics(topicList);
        pcMember = util.pcMemberRepository.save(pcMember);
        return pcMember;
    }

    public PcMember queryPcMember(String confname, String username) {
        return util.pcMemberRepository.
                findByUserAndConference(queryUser(username), queryConference(confname));
    }
    //endregion

    //region author add & query
    public Author addAuthor(String confname, String username) {
        Author author = new Author(queryUser(username), queryConference(confname));
        author = util.authorRepository.save(author);
        return author;
    }

    public Author queryAuthor(String confname, String username) {
        return util.authorRepository.
                findByUserAndConference(queryUser(username), queryConference(confname));
    }
    //endregion

    //region topic add &query
    public Topic addTopic(String confname, String topic) {
        Topic tpc = new Topic(topic, queryConference(confname));
        tpc = util.topicRepository.save(tpc);
        tpcs.get(confname).put(topic, tpc.getId());
        return tpc;
    }

    public Collection<Topic> addTopic(String confname, String... topics) {
        final ArrayList<Topic> list = new ArrayList<>(topics.length);
        for (String topic : topics) {
            list.add(addTopic(confname, topic));
        }
        return list;
    }

    public Topic queryTopic(String confname, String topic) {
        final Optional<Topic> optional = util.topicRepository.findById(tpcs.get(confname).get(topic));
        if (!optional.isPresent()) {
            throw new IllegalArgumentException(
                    String.format(
                            "Topic %s not in conference %s", topic, confname
                    ));
        }
        return optional.get();
    }

    public List<Topic> queryTopic(String confname, String... topics) {
        ArrayList<Topic> tpcList = new ArrayList<>(topics.length);
        for (String topic : topics) {
            tpcList.add(queryTopic(confname, topic));
        }
        return tpcList;
    }
    //endregion

    //region paper add & query
    public Paper addPaper(String confname, String authorname, String title, String... topics) {
        final Author author = queryAuthor(confname, authorname);
        final List<Topic> topicList = queryTopic(confname, topics);
        Paper paper =
                new Paper(title, title, title, author, queryConference(confname));
        paper.getTopics().addAll(topicList);
        paper = util.paperRepository.save(paper);
        pps.get(confname).put(title, paper.getId());

        return paper;
    }

    public Paper queryPaper(String confname, String title) {
        final Optional<Paper> optional = util.paperRepository.findById(pps.get(confname).get(title));
        if (!optional.isPresent()) {
            throw new IllegalArgumentException(
                    String.format(
                            "Paper %s not in conference %s", title, confname
                    ));
        }
        return optional.get();
    }
    //endregion

    //region writers add
    public List<Writer> addWritersToPaper(String confname, String title, String... writers) {
        ArrayList<Writer> writerArrayList = new ArrayList<>(writers.length);

        int index = 0;
        final Paper paper = queryPaper(confname, title);
        Writer wtr;
        for (String writer : writers) {
            wtr = new Writer(writer, qqMail(writer), writer, writer);
            wtr.setIndex(index);
            wtr.setPaper(paper);
            writerArrayList.add(util.writerRepository.save(wtr));
        }

        return writerArrayList;
    }
    //endregion

    //region papers allocate

    public Conference changeStrategy(String confname, Conference.AllocationStrategy strategy) {
        final Conference conference = queryConference(confname);
        conference.setStrategy(strategy);
        return util.conferenceRepository.save(conference);
    }

    public boolean allocatePapers(String confname) {
        return paperAllocateService.process(queryConference(confname));
    }
    //endregion

    //region reviewInfo add & query & update
    public ReviewInfo addReviewInfoToPcMember(String confname, String title, String username) {
        final ReviewInfo reviewInfo =
                new ReviewInfo(queryPaper(confname, title), queryPcMember(confname, username));
        return util.reviewInfoRepository.save(reviewInfo);
    }


    public ReviewInfo queryReviewInfoFromPcMember(String confname, String title, String username) {
        return util.reviewInfoRepository.
                findByPcMemberUserAndPaperId(queryUser(username), pps.get(confname).get(title));
    }

    public ReviewInfo updateReviewInfo(ReviewInfo reviewInfo,
                                       boolean completed,
                                       ReviewInfo.ReviewInfoState state,
                                       ReviewInfo.ReviewConfidence confidence,
                                       String comment,
                                       int version) {
        reviewInfo.setCompleted(completed);
        reviewInfo.setReviewInfoState(state);
        reviewInfo.setReviewConfidence(confidence);
        reviewInfo.setComment(comment);
        reviewInfo.setVersion(version);
        return util.reviewInfoRepository.save(reviewInfo);
    }
    //endregion

    //region post add & query & reply
    public Post addPost(String confname, String title) {
        return util.postRepository.save(new Post(queryPaper(confname, title), title));
    }

    public Post queryPost(String confname, String title) {
        return util.postRepository.findByPaperId(queryPaper(confname, title).getId());
    }

    public Post replyPost(String confname, String title, String username) {
        final Post post = queryPost(confname, title);
        Reply reply = new Reply(post, queryPcMember(confname, username), username);
        reply = util.replyRepository.save(reply);
        return reply.getPost();
    }

    //endregion

    //region rebuttal add & query
    public Rebuttal addRebuttal(String confname, String title) {
        final Post post = addPost(confname, title);
        final Rebuttal rebuttal = new Rebuttal(post, title);

        return util.rebuttalRepository.save(rebuttal);
    }

    public Rebuttal queryRebuttal(String confname, String title) {
        return util.rebuttalRepository.
                findByPostPaperId(queryPaper(confname, title).getId());
    }
    //endregion
}
